﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Security.Principal;
using System.ServiceModel;
using System.Text.RegularExpressions;
using itau.im.entidade;
using System.ServiceModel.Web;

namespace Itau.IM.CAS
{
    public class IntegracaoCAS : IDisposable
    {
        #region Atributos

        private AdminReference.ControleAcessoAdminServiceClient _client;
        private string _siglaSistema;
        private List<string> _acessosUsuario;
        private List<int> _perfisUsuario;

        #endregion Atributos

        #region Propriedades

        public bool UsuarioAdministrador
        {
            get
            {
                if (!Convert.ToBoolean(ConfigurationManager.AppSettings["CAS.VerificarAcessos"]))
                {
                    return true;
                }

                int codigoPerfilAdministrador = Convert.ToInt32(ConfigurationManager.AppSettings["CAS.PerfilAdministrador"]);

                if (this._perfisUsuario.Count == 0)
                {
                    PreencherAcessosUsuario();
                }

                return this._perfisUsuario.Contains(codigoPerfilAdministrador);
            }
        }

        #endregion Propriedades

        #region Métodos

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="controle_">Tela cliente do controle de acesso</param>
        public IntegracaoCAS()
        {
            this._client = new Itau.IM.CAS.AdminReference.ControleAcessoAdminServiceClient();
            this._siglaSistema = ConfigurationManager.AppSettings["CAS.SiglaSistema"];
            this._acessosUsuario = new List<string>();
            this._perfisUsuario = new List<int>();
        }

        /// <summary>
        /// Método responsável por gerar um código de declaração de acesso
        /// </summary>
        /// <param name="dominio_">Domínio do objeto</param>
        /// <param name="objeto_">Código de objeto</param>
        /// <param name="tipoAcesso_">Tipo de acesso ao objeto</param>
        /// <param name="grupo_">Perfil de acesso</param>
        /// <returns>Código de declaração</returns>
        public static string GerarCodigoAcesso(DominioAcesso dominio_, int objeto_, TipoAcesso tipoAcesso_, int grupo_)
        {
            return String.Format("{0}|{1}|{2}|{3}", (int)dominio_, objeto_, (int)tipoAcesso_, grupo_);
        }

        public void PreencherAcessosUsuario()
        {
            string usuarioRequisicao = String.Empty;
            
            if (WebOperationContext.Current != null)
            {
                usuarioRequisicao = WebOperationContext.Current.IncomingRequest.Headers["usuario"];
            }
            else
            {
                //Mock para teste unitário
                usuarioRequisicao = ConfigurationManager.AppSettings["CAS.Mock.Username"].ToString();
            }

            //Identificando a quais perfis o usuário pertence
            foreach (AdminReference.Perfil perfil in this._client.SelecionarPerfisPorSistema(this._siglaSistema))
            {
                foreach (AdminReference.Usuario usuario in this._client.SelecionarUsuariosPorPerfil(perfil.Id))
                {
                    if (usuario.Codigo.ToLower().Equals(usuarioRequisicao))
                    {
                        this._perfisUsuario.Add(perfil.Id);
                    }
                }
            }

            //Populando acessos do usuário
            foreach (int codigoPerfil in this._perfisUsuario)
            {
                foreach (AdminReference.Declaracao declaracao in this._client.SelecionarDeclaracaoPorPerfil(codigoPerfil))
                {
                    this._acessosUsuario.Add(declaracao.Codigo);
                }
            }
        }

        /// <summary>
        /// Método responsável por verificar se um usuário tem acesso a um determinado objeto
        /// </summary>
        /// <param name="dominio_">Domínio do objeto</param>
        /// <param name="objeto_">Código do objeto</param>
        /// <param name="tipoAcesso_">Tipo de acesso</param>
        /// <returns>Permissão de acesso</returns>
        public bool VerificarAcessoUsuario(DominioAcesso dominio_, int objeto_, TipoAcesso tipoAcesso_)
        {
            //Caso a integração com o CAS esteja desabilitada, não há a necessidade de verificar suas permissões
            if (!Convert.ToBoolean(ConfigurationManager.AppSettings["CAS.VerificarAcessos"]))
            {
                return true;
            }

            //Caso o usuário seja um administrador não há necessidade de verificar suas permissões
            if (UsuarioAdministrador)
            {
                return true;
            }

            //Verificando se o usuário possui acesso ao objeto
            string prefixo = String.Format("{0}|{1}|{2}", (int)dominio_, objeto_, (int)tipoAcesso_);

            foreach (string acesso in this._acessosUsuario)
            {
                if (acesso.StartsWith(prefixo))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Método responsável por filtrar os registros de uma lista utilizando o Controle de Acesso a Sistemas
        /// </summary>
        /// <param name="curvas_">Lista de Curvas</param>
        /// <returns>Lista de registros filtrados</returns>
        public IList<T> FiltrarRegistros<T>(IList<T> objetos_, DominioAcesso dominioAcesso_, TipoAcesso tipoAcesso_)
        {
            //Caso a integração com o CAS esteja desabilitada, não há a necessidade de verificar suas permissões
            if (!Convert.ToBoolean(ConfigurationManager.AppSettings["CAS.VerificarAcessos"]))
            {
                return objetos_;
            }

            //Caso o usuário seja um administrador não há necessidade de verificar suas permissões
            if (UsuarioAdministrador)
            {
                return objetos_;
            }
            else
            {
                IList<T> listaFiltrada = new List<T>();

                foreach (T item in objetos_)
                {
                    int codigo = 0;

                    switch (typeof(T).Name)
                    {
                        case "Curva":
                            Curva curvaTemp = (Curva)Convert.ChangeType(item, typeof(Curva));
                            codigo = curvaTemp.CodigoCurva;
                            break;

                        case "CurvaVigente":
                            Curva curvaVigenteTemp = (Curva)Convert.ChangeType(item, typeof(Curva));
                            codigo = curvaVigenteTemp.CodigoCurva;
                            break;

                        case "ParametroAtribuicao":
                            ParametrizacaoAtribuicao parametroTemp = (ParametrizacaoAtribuicao)Convert.ChangeType(item, typeof(ParametrizacaoAtribuicao));
                            codigo = parametroTemp.CodigoParametroAtribuicao;
                            break;

                        case "GrupoParametroAtribuicao":
                            GrupoParametroAtribuicao grupoTemp = (GrupoParametroAtribuicao)Convert.ChangeType(item, typeof(GrupoParametroAtribuicao));
                            codigo = grupoTemp.CodigoGrupoParametroAtribuicao;
                            break;
                    }

                    if (VerificarAcessoUsuario(dominioAcesso_, codigo, tipoAcesso_))
                    {
                        listaFiltrada.Add(item);
                    }
                }

                //Retorna lista com dados filtrados
                return listaFiltrada;
            }
        }

        #endregion Métodos

        #region IDisposable Members

        public void Dispose()
        {
            this._client = null;
            this._siglaSistema = null;
            this._acessosUsuario = null;
            this._perfisUsuario = null;
        }

        #endregion
    }

    #region Classes Auxiliares

    /// <summary>
    /// Classe responsável pela criação de instâncias
    /// </summary>
    public class Factory
    {
        #region Métodos

        /// <summary>
        /// Método responsável por criar uma instância do tipo LiberacaoAcesso
        /// </summary>
        /// <param name="declaracao_">Declaração do CAS</param>
        /// <returns>Instância de LiberacaoAcesso</returns>
        public LiberacaoAcesso CriarLiberacaoAcesso(AdminReference.Declaracao declaracao_)
        {
            if (!declaracao_.Codigo.Contains("|"))
            {
                return null;
            }

            string[] dadosAcesso = declaracao_.Codigo.Split('|');

            return new LiberacaoAcesso(
                (DominioAcesso)Convert.ToInt32(dadosAcesso[0]),
                Convert.ToInt32(dadosAcesso[1]),
                Convert.ToInt32(dadosAcesso[3]),
                (TipoAcesso)Convert.ToInt32(dadosAcesso[2]),
                declaracao_.Descricao);
        }

        #endregion Métodos
    }

    public class LiberacaoAcesso
    {
        #region Atributos

        private int _objeto;
        private int _grupo;
        private TipoAcesso _tipoAcesso;
        private string _descricao;
        private string _codigo;

        #endregion Atributos

        #region Propriedades

        public int Objeto { get { return this._objeto; } }
        public int Grupo { get { return this._grupo; } }
        public TipoAcesso TipoAcesso { get { return this._tipoAcesso; } }
        public string Descricao { get { return this._descricao; } }
        public string Codigo { get { return this._codigo; } }

        #endregion Propriedades

        #region Métodos

        /// <summary>
        /// Construtor
        /// </summary>
        public LiberacaoAcesso(DominioAcesso dominio_, int objeto_, int grupo_, TipoAcesso tipoAcesso_, string descricao_)
        {
            this._objeto = objeto_;
            this._grupo = grupo_;
            this._tipoAcesso = tipoAcesso_;
            this._descricao = descricao_;

            this._codigo = IntegracaoCAS.GerarCodigoAcesso(dominio_, objeto_, tipoAcesso_, grupo_);
        }

        #endregion Métodos
    }

    #endregion Classes Auxiliares

    #region Enumeradores

    /// <summary>
    /// Tipo de acesso a objetos
    /// </summary>
    public enum TipoAcesso
    {
        Consulta = 1,
        Gravacao = 2,
        Ativacao = 3
    }

    /// <summary>
    /// Domínios de acesso a objetos
    /// </summary>
    public enum DominioAcesso
    {
        Curva = 1,
        Parametro = 2,
        GrupoParametro = 3
    }

    #endregion Enumeradores
}